/**
 * 20220301:simplified from [UIComponent,ThreejsContainer]
 * 20220610:1.old canvas-ui has no practical use for now.
 *          2.compute rect width evets-area.
 * 20220614:1.change loading-ui name of closing-method.
 *          2.remove deprecated contructor.
 *          3.use VR-supporting dom-cross-platform-event.
 * 20221010:rewrite with typescript, massive modified.
 */
import React from 'react';
import {CrossEvent} from './dom-cross-platform-event';
import WEBGL from 'three/examples/jsm/capabilities/WebGL';//webGPU on the way
import assetsLoad from '../loader/assets-load';
import { WebGL1Renderer, WebGLRenderer, WebGLRendererParameters } from 'three';
import {AssetsLoader} from '../loader/assets-loader';

interface Handler{
    (e?:any):void;
}

export default abstract class ThreeContainer extends React.PureComponent {

    private resizeChange!:Handler;

    protected canvasContainerRef!:React.RefObject<HTMLDivElement>;
    protected eventAreaRef!:React.RefObject<HTMLDivElement>;
    protected clientRect!:DOMRect;

    public renderer!:WebGLRenderer;
    public crossEvent!:CrossEvent;
    public size!:DOMRect;
    public assetsLoader!:AssetsLoader;

    protected get ui():React.ReactNode{
        return null;
    }
    protected abstract resize(size:DOMRect):void;

    public copyDomRect(f:DOMRect,t:DOMRect):void{
        t.x=f.x;
        t.y=f.y;
        t.width=f.width;
        t.height=f.height;
    }

    public startLoad(message?:string):void{}

    public loading(message?:string,progress?:number):void{}

    public loadError(message?:string):void{}

    public loaded():void{
        const event=new Event('asset-loaded');
        window.dispatchEvent(event);
    }

    private get children() {
        return <React.Fragment>
            <div key="canvas_container" ref={this.canvasContainerRef=this.canvasContainerRef||React.createRef<HTMLDivElement>()}
                style={{position:'absolute',left:'0',right:'0',top:'0',bottom:'0',overflow:'hidden',lineHeight:'normal'}}/>
            <div key='event_area' ref={this.eventAreaRef=this.eventAreaRef||React.createRef<HTMLDivElement>()}
                style={{position:"absolute",left:"0px",top:"0px",width:"100%",height:"100%"}}/>
            <div key="canvas_ui" style={{position:'absolute',left:'0',top:'0',width:'100%',height:'100%',pointerEvents:'none'}}>
                {this.ui} 
            </div>
            </React.Fragment>
    }

    get devicePixelRatio(){
        return Math.max(Math.min(window.devicePixelRatio,3),1);
    }

    protected initVariable():void {
        this.clientRect = new DOMRect();
        this.resizeChange = () => {
            const currentRect = new DOMRect();
            if (this.eventAreaRef) {
                const bound = this.eventAreaRef.current!.getBoundingClientRect();
                this.copyDomRect(bound,currentRect);
            }
            let posChange = false;
            let sizeChange = false;
            if (currentRect.left !== this.clientRect.left || currentRect.top !== this.clientRect.top) {
                posChange = true;
            }
            if (currentRect.width !== this.clientRect.width || currentRect.height !== this.clientRect.height) {
                sizeChange = true;
            }
            if (!posChange && !sizeChange) {
                return;
            }
            this.clientRect = currentRect;
            this.clientRectChange(sizeChange, posChange);
        };
    }

    protected getRendererParameters():WebGLRendererParameters{
        return {};
    }
    protected abstract getAssetsUrl():any;

    private initialize():void{
        if(WEBGL.isWebGL2Available()){
            this.renderer = new WebGLRenderer(this.getRendererParameters());
        }else if(WEBGL.isWebGLAvailable()){
            this.renderer = new WebGL1Renderer(this.getRendererParameters());
        }else{
            alert("your browser does not support WebGL!");
            return;
        }
        this.renderer.setPixelRatio(this.devicePixelRatio);
        this.renderer.setSize(this.size.width,this.size.height);
        this.canvasContainerRef.current!.appendChild(this.renderer.domElement);
        this.resize(this.size);
        this.loadAssets();
    }

    private loadAssets():void{
        assetsLoad(this,this,this.getAssetsUrl).then(
            ()=>{
                this.initComponents(this.assetsLoader.assets);
                this.prepared();
                this.renderer.setAnimationLoop(this.animate.bind(this));
            },
            (err)=>{
                console.warn('failed to load assets!',err);
            }
        );
    }
    protected abstract initComponents(assets:any):void;
    protected prepared():void{
    }
    protected abstract animate(time:number,frame:XRFrame):void;

    public render():React.ReactNode {
        return (
            <div ref='parent' >
                {this.children}
            </div>
        );
    }
   
    public componentDidMount():void {     
        this.initVariable();
        
        window.addEventListener("resize", this.resizeChange, {passive: false});
        window.addEventListener("scroll", this.resizeChange, true);
        this.resizeChange();
        document.oncontextmenu = ()=>{
            return false;
        };
        this.crossEvent = new CrossEvent(this.eventAreaRef.current!);
        this.crossEvent.start();
        this.initialize();
    }
    public componentWillUnmount():void{
        window.removeEventListener("resize", this.resizeChange);
        window.removeEventListener("scroll", this.resizeChange);
        this.crossEvent.dispose();
    }

    private clientRectChange(sizeChange:boolean,posChange:boolean):void{
        this.size = this.canvasContainerRef.current!.getBoundingClientRect();
        if(!this.renderer)return;
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setSize(this.size.width, this.size.height);
        this.resize(this.size);
    }
  
}